#include "sgw_ubus.h"

#ifdef USE_OPENWRT_UBUS
// #include <rpcd/plugin.h>
#include "sgwService.h"
#include "version.h"

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <libubus.h>
#include <libubox/uloop.h>
#include <libubox/blobmsg_json.h>
#include <arpa/inet.h>
#include <termios.h>

#include "sgw_device.h"
#include "aexudp.h"
#include "mod_gpio.h"
 
// #include "test.h"
// #include "debug.h"
// #include "pubinfo.h"
 
struct ubus_context *ubus_ctx = NULL;
 
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
 
enum {GET_TYPE_FLOW, GET_TYPE_SPEED};
 
#define _unused __attribute__((unused))
#define _packed __attribute__((packed))
 
static struct blob_buf b;
 
 
/**********************************************************************
 * pub policy
 *********************************************************************/
enum {
	UBUS_SGW_PUB_TOPIC,
	UBUS_SGW_PUB_MSG,
	__UBUS_SGW_PUB_MAX
};
static const struct blobmsg_policy sgw_pub_policy[] ={
	[UBUS_SGW_PUB_TOPIC] = { .name = "topic", .type = BLOBMSG_TYPE_STRING },
	[UBUS_SGW_PUB_MSG]   = { .name = "msg",   .type = BLOBMSG_TYPE_TABLE },
};
 
/**********************************************************************
 * Stats policy
 *********************************************************************/
enum {
	UBUS_STATS_OBJS,
	__UBUS_STATS_MAX
};
static const struct blobmsg_policy stats_pub_policy[] ={
	[UBUS_STATS_OBJS] = { .name = "obj", .type = BLOBMSG_TYPE_ARRAY },
};

enum {
	UBUS_SGW_CONFIG,
	__UBUS_SGW_CONFIG_MAX
};
static const struct blobmsg_policy sgw_config_policy[] ={
	[UBUS_SGW_CONFIG] = { .name = "session", .type = BLOBMSG_TYPE_STRING },
};

/**********************************************************************
 * sub policy
 *********************************************************************/
enum {
	UBUS_SGW_SUB_TOPICS,
	__UBUS_SGW_SUB_MAX
};
static const struct blobmsg_policy sgw_sub_policy[] ={
	[UBUS_SGW_SUB_TOPICS] = { .name = "topics", .type = BLOBMSG_TYPE_ARRAY },
};
 
/**********************************************************************
 * subscribe method: subscribe a list of topics
 *********************************************************************/
static int ubus_sgw_sub(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req, const char *method,
		struct blob_attr *msg)
{
	unsigned ret=0;
	struct blob_attr *tb[__UBUS_SGW_SUB_MAX];
	blobmsg_parse(sgw_sub_policy, __UBUS_SGW_SUB_MAX, tb, blob_data(msg), blob_len(msg));
 
	if (!tb[UBUS_SGW_SUB_TOPICS]){
		hcs_error("ubus smartgw sub: no arguments!");
		goto out;
	}
	// update_test_topic_list(tb[UBUS_SGW_SUB_TOPICS]);
	ret=1;
out:
	blob_buf_init(&b, 0);
	blobmsg_add_u8(&b, "result", ret);
	ubus_send_reply(ctx, req, b.head);
	return 0;
}

/**********************************************************************
 * publish method: publish messages
 *********************************************************************/
static int ubus_sgw_pub(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req, const char *method,
		struct blob_attr *msg)
{
	int ret=0;
	struct blob_attr *tb[__UBUS_SGW_PUB_MAX];
	char *message="", *topic;
	blobmsg_parse(sgw_pub_policy, __UBUS_SGW_PUB_MAX, tb, blob_data(msg), blob_len(msg));
 
	if (!tb[UBUS_SGW_PUB_TOPIC]) {
		goto out;
	}
	topic=blobmsg_get_string(tb[UBUS_SGW_PUB_TOPIC]);
 
	if (tb[UBUS_SGW_PUB_MSG]!=NULL) {
		message=(char*)blobmsg_data(tb[UBUS_SGW_PUB_MSG]);
	}
 
	// test_pub_msg(topic, message);
	hcs_hint("PUB: topic=%s msg=%s\n",topic,message);
 
	ret=1;
out:
	blob_buf_init(&b, 0);
	blobmsg_add_u8(&b, "result", ret);
	ubus_send_reply(ctx, req, b.head);
	return 0;
}
 
/**********************************************************************
 * publish method: simple publish messages
 *********************************************************************/
static int ubus_sgw_spub(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req, const char *method,
		struct blob_attr *msg)
{
	int ret=0;
	struct blob_attr *tb[__UBUS_SGW_PUB_MAX];
	char *message="", *cmd;
	blobmsg_parse(sgw_pub_policy, __UBUS_SGW_PUB_MAX, tb, blob_data(msg), blob_len(msg));
 
	if (!tb[UBUS_SGW_PUB_TOPIC]){
		hcs_error("ubus call spub without topic");
		goto out;
	}
	cmd=blobmsg_get_string(tb[UBUS_SGW_PUB_TOPIC]);
 
	if (tb[UBUS_SGW_PUB_MSG]!=NULL){
		// message=blobmsg_format_json(tb[UBUS_SGW_PUB_MSG], true);
	}
 
	// simple_pub_msg(cmd, message, 1);
	ret=1;
out:
	blob_buf_init(&b, 0);
	blobmsg_add_u8(&b, "result", ret);
	ubus_send_reply(ctx, req, b.head);
	return 0;
}
 
 
/**********************************************************************
 * list method: list current subscribe topic
 *********************************************************************/
static int ubus_sgw_list(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req, const char *method,
		struct blob_attr *msg)
{
	// get_topic_list(&b);
	ubus_send_reply(ctx, req, b.head);
	return 0;
}
 
/**********************************************************************
 * ubus method: ubus_pub_stats
 *********************************************************************/
static int ubus_pub_stats(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req, const char *method,
		struct blob_attr *msg)
{
	int ret = 0;
	struct blob_attr *tb[__UBUS_STATS_MAX];
 
	blobmsg_parse(stats_pub_policy, __UBUS_STATS_MAX, tb, blob_data(msg), blob_len(msg));
 
	if (tb[UBUS_STATS_OBJS]) {
		// pub_info(tb[UBUS_STATS_OBJS]);
		ret = 1;
	}
	
	blob_buf_init(&b, 0);
	blobmsg_add_u8(&b, "result", ret);
	ubus_send_reply(ctx, req, b.head);
	
	return 0;
}
 
/**********************************************************************
 * list method: list current subscribe topic
 *********************************************************************/
static int ubus_smartgw_test(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req, const char *method,
		struct blob_attr *msg)
{
	int status;
	// get_topic_list(&b);
 
	hcs_hint("==>begin wait status");
	wait(&status);
	hcs_hint("==>after wait status:%d", status);
	
    blob_buf_init(&b, 0);
	blobmsg_add_u8(&b, "result", 1);
    blobmsg_add_string(&b,"message","success");
	ubus_send_reply(ctx, req, b.head);
	return 0;
}
 
static int ubus_smartgw_get_config(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req, const char *method,
		struct blob_attr *msg)
{	
    hcs_config *psudata = get_hcs_data();

    blob_buf_init(&b, 0);
    blobmsg_add_u32(&b,"slotaddr",aexgpio_get_module_addr());
	blobmsg_add_string(&b,"uuid", psudata->main_uuid);
    blobmsg_add_string(&b,"version",APP_VERSION_STR);
    blobmsg_add_u32(&b,"vernum",APP_VERSION_NUM);
	ubus_send_reply(ctx, req, b.head);
	return 0;
}
 
static int ubus_smartgw_get_devices(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req, const char *method,
		struct blob_attr *msg)
{	
    char buf[1024];
	hcs_config *psudata = get_hcs_data();

    sgw_save_devices_tobuf(buf,sizeof(buf));
    blob_buf_init(&b, 0);
    	// blobmsg_add_object(&b,sgw_get_blob_devices());
	blobmsg_add_json_from_string(&b,buf);
	ubus_send_reply(ctx, req, b.head);
	// json_object_put(devices);
	return 0;
}

 
static int ubus_smartgw_save_devices(struct ubus_context *ctx, struct ubus_object *obj,
		struct ubus_request_data *req, const char *method,
		struct blob_attr *msg)
{	
    hcs_config *psudata = get_hcs_data();

    int len = sgw_save_devices_tofile(psudata->devices_fn);
    blob_buf_init(&b, 0);
	blobmsg_add_u32(&b, "len", len);
	ubus_send_reply(ctx, req, b.head);
	return 0;
}

/**********************************************************************
 * smartgw method 
 *********************************************************************/
static const struct ubus_method sgw_methods[] = {
	UBUS_METHOD("pub",ubus_sgw_pub,sgw_pub_policy),
	UBUS_METHOD("spub",ubus_sgw_spub,sgw_pub_policy),
	UBUS_METHOD("sub",ubus_sgw_sub,sgw_sub_policy),
	UBUS_METHOD("pubstats",ubus_pub_stats, stats_pub_policy),
	UBUS_METHOD_NOARG("lsub",ubus_sgw_list),
    UBUS_METHOD_NOARG("get_config",ubus_smartgw_get_config),
    UBUS_METHOD_NOARG("get_devices",ubus_smartgw_get_devices),
    UBUS_METHOD_NOARG("save_devices",ubus_smartgw_save_devices),
	UBUS_METHOD_NOARG("test",ubus_smartgw_test),
};
 
/**********************************************************************
 * test object
 *********************************************************************/
static struct ubus_object_type sgw_object_type =
	UBUS_OBJECT_TYPE("smartgw", sgw_methods);
 
static struct ubus_object sgw_object = {
	.name = "smartgw",
	.type = &sgw_object_type,
	.methods = sgw_methods,
	.n_methods = ARRAY_SIZE(sgw_methods),
};
 
static void ubus_connect_handler(struct ubus_context *ctx)
{
	ubus_ctx = ctx;
 
	hcs_hint("ubus now connected.");
	ubus_add_uloop(ctx);
	ubus_add_object(ctx, &sgw_object);
	
	return;
}
 
static struct ubus_auto_conn conn;
 
static void receive_ubus_data(struct ubus_request *req, int type, struct blob_attr *msg)
{
	struct blob_attr **ret = (struct blob_attr **)req->priv;
	*ret = blob_memdup(msg); 
}
 
int ubus_send(const char *type, struct blob_attr *data)
{
	if (!ubus_ctx || !type || !data) {
		return -1;
	}
	return ubus_send_event(ubus_ctx, type, data);
}
 
int ubus_call(const char *path, const char *method,
		struct blob_attr *data, struct blob_attr **ret)
{
	uint32_t id;
	int      _ret;
 
	if (ubus_ctx == NULL) {
		return -1;
	}
 
	_ret = ubus_lookup_id(ubus_ctx, path, &id);
	if (_ret) {
		hcs_hint("lookup stats id error!");
		return -1;
	}
	
	return ubus_invoke(ubus_ctx, id, method, data, receive_ubus_data, ret, 1000);
}
 
int ubus_init(void)
{
	conn.cb = ubus_connect_handler;
	ubus_auto_connect(&conn);
 
	return 0;
}
 
void ubus_destory()
{
	/*
	ubus_remove_object(ubus, &main_object);
	ubus_free(ubus);
	*/
}

/**
 * 定义了一个定时器，使用uloop实现定时器功能，此函数为定时器回调函数。
 */
void timeout_callback(struct uloop_timeout *timeout)
{
    // hcs_hint("timeout_callback\r\n");
   
    uloop_timeout_set(timeout, 1000);
}

void heart_callback(struct uloop_timeout *timeout)
{
	hcs_config *psudata = get_hcs_data();
	char buf[256];
	int hdelay = StrToInt(psudata->server_heart);

	if(hdelay==0)hdelay=240;
    // hcs_hint("timeout_callback\r\n");
	aexudp_buid_heart_packnet();
	// mqtt_pub(buf,AEXUDP_GET_PACKET_LEN(buf));
	// SendtoServer(buf,AEXUDP_GET_PACKET_LEN(buf));
    uloop_timeout_set(timeout, hdelay*1000);
}

static char uartPacknetBuf[1024];
static struct ringbuffer uartPacknetFifo;
/**
 * 边缘计算板底板使用UART3，两个RS485使用UART4和UART5
 */
void read_uart3_callback(struct uloop_fd *u, unsigned int events)
{
    unsigned char buffer[AEXUDP_PACKNET_MAX_PLOAD] = {0};
    int len = 0;
    
    if (ULOOP_READ) {
        len = read(u->fd, buffer, AEXUDP_PACKNET_MAX_PLOAD);
        if (len > 0) 
        {
			parse_packnet_cmd(&uartPacknetFifo,buffer,len);
        }
    }
}

void read_uart4_callback(struct uloop_fd *u, unsigned int events)
{
    unsigned char buf[AEXUDP_PACKNET_MAX_PLOAD] = {0};
	unsigned char *pdata = AEXUDP_DATA_GET_PAYLOAD(AEXUDP_GET_DATA(buf));
	unsigned char len=AEXUDP_UART_MAX_LEN;
	PPKTPROPO handle = aexudp_get_default_prkpropo();

	hcs_hint("%s\r\n",__func__);
    if ((events&ULOOP_READ) == ULOOP_READ) {
        len = read(u->fd, &pdata[1], len);
        if (len > 0) 
        {
			unsigned char index=0;

			pdata[0] = 0;
			AEXUDP_SET_BEGIN(buf,handle->start);
			AEXUDP_SET_INDEX(buf,aexudp_get_packnet_index());
			AEXUDP_SET_CMD(buf,AEX_CMD_TRANSDATA);
			AEXUDP_DATA_SET_SRC(AEXUDP_GET_DATA(buf),sgw_get_mod_addr());
			AEXUDP_DATA_SET_DST(AEXUDP_GET_DATA(buf),0);				//边缘计算的心跳是发给服务器的
			AEXUDP_SET_LEN(buf,len+3);
			if(handle->encrypt){
				handle->encrypt(buf,AEXUDP_GET_LEN(buf));
			}else{
				AEXUDP_SET_CRC(buf);
			}
			AEXUDP_SET_END(buf,handle->end);

            SendtoServer(buf,AEXUDP_GET_PACKET_LEN(buf));
        }
    }
}

void read_uart5_callback(struct uloop_fd *u, unsigned int events)
{
    unsigned char buf[AEXUDP_PACKNET_MAX_PLOAD] = {0};
	unsigned char *pdata = AEXUDP_DATA_GET_PAYLOAD(AEXUDP_GET_DATA(buf));
	unsigned char len=AEXUDP_UART_MAX_LEN;
	PPKTPROPO handle = aexudp_get_default_prkpropo();

	hcs_hint("%s begin\r\n",__func__);
    if ((events&ULOOP_READ) == ULOOP_READ) {
		hcs_hint("%d:uart_fd=%d,len=%d\r\n",__LINE__,u->fd,len);
        len = read(u->fd, &pdata[1], len);
		hcs_hint("%d:uart_fd=%d,len=%d\r\n",__LINE__,u->fd,len);
        if (len > 0) 
        {
			unsigned char index=0;

			// hcs_hint("uart_fd=%d,len=%d\n",u->fd,len);
			pdata[0] = 1;		
			AEXUDP_SET_BEGIN(buf,handle->start);
			AEXUDP_SET_INDEX(buf,aexudp_get_packnet_index());
			AEXUDP_SET_CMD(buf,AEX_CMD_TRANSDATA);
			AEXUDP_DATA_SET_SRC(AEXUDP_GET_DATA(buf),sgw_get_mod_addr());
			AEXUDP_DATA_SET_DST(AEXUDP_GET_DATA(buf),0);				//边缘计算的心跳是发给服务器的
			AEXUDP_SET_LEN(buf,len+3);
			if(handle->encrypt){
				handle->encrypt(buf,AEXUDP_GET_LEN(buf));
			}else{
				AEXUDP_SET_CRC(buf);
			}
			AEXUDP_SET_END(buf,handle->end);

            SendtoServer(buf,AEXUDP_GET_PACKET_LEN(buf));
        }
    }
	hcs_hint("%s end.\r\n",__func__);
}

void read_tcp_callback(struct uloop_fd *u, unsigned int events)
{
    unsigned char buffer[AEXUDP_PACKNET_MAX_PLOAD] = {0};
    int len = 0;
    
    if ((events&ULOOP_READ) == ULOOP_READ) {
        len = read(u->fd, buffer, AEXUDP_PACKNET_MAX_PLOAD);
        if (len > 0) 
        {
        }
    }
}

static char udpPacknetBuf[1024];
static struct ringbuffer udpPacknetFifo;
void read_udp_callback(struct uloop_fd *u, unsigned int events)
{
    unsigned char buffer[AEXUDP_PACKNET_MAX_PLOAD] = {0};
    int len = 0;
    
    if ((events&ULOOP_READ) == ULOOP_READ) {
		len = aexudp_recv_from_udp(u->fd,buffer, sizeof(buffer));
		if(len > 0)
		{
			// 解析处理边云测控协议
			parse_packnet_cmd(&udpPacknetFifo, buffer, len);
		}
    }
}

#endif //USE_OPENWRT_UBUS

void rpc_smartgw_init()
{
	hcs_config *pconfig= get_hcs_data();
#ifdef USE_OPENWRT_UBUS
	int fd_uart3=-1,fd_uart4=-1,fd_uart5=-1;

    hcs_hint("%s\r\n",__func__);

	fd_uart3 = OpenUart(pconfig);

	struct uloop_fd fd_uart_uart3 = {
		.cb = read_uart3_callback,
		.fd = fd_uart3,
		.flags = ULOOP_READ,
	};

	pconfig->hcsdata.tcp_fd = StartTcpSocket(pconfig);
	pconfig->hcsdata.udp_fd = StartUdpSocket(pconfig);
	struct uloop_fd fd_net_tcp = {
		.cb = read_tcp_callback,
		.fd = pconfig->hcsdata.tcp_fd,
	};
	struct uloop_fd fd_net_udp = {
		.cb = read_udp_callback,
		.fd = pconfig->hcsdata.udp_fd,
	};


	struct uloop_timeout fd_timeout = {
		.cb = timeout_callback,
	};

	struct uloop_timeout heart_timeout = 
	{
		.cb = heart_callback,
	};
	

	uloop_init();
    uloop_timeout_set(&fd_timeout, 1000); //实现一个秒脉冲
    uloop_timeout_set(&heart_timeout, StrToInt(pconfig->server_heart)*1000); //实现heart
	uloop_fd_add(&fd_uart_uart3, ULOOP_READ);
    // if(pconfig->hcsdata.uarts[0].type == AEX_UART_RS232){
	// 	//设置 RS232 串口
	// 	aexgpio_onboard_set(SGWEC_CH1_SEL,1);
	// }else{
	// 	//设置 RS485 串口
	// 	aexgpio_onboard_set(SGWEC_CH1_SEL,0);
	// }
	struct uloop_fd fd_uart_uart4 = {
		.cb = read_uart4_callback,
		.fd = fd_uart4,
		.flags = ULOOP_READ,
	};
	fd_uart4 = com_open(pconfig->uart4_port,O_RDWR | O_NOCTTY | O_NONBLOCK);
    if(fd_uart4 > 0)
	{
		fd_uart_uart4.fd = fd_uart4;
		pconfig->hcsdata.uarts[0].uart_fd = fd_uart4;
     	uloop_fd_add(&fd_uart_uart4, ULOOP_READ);
    }else{
		hcs_error("open uart4 error:%s", strerror(errno));
	}
    // if(pconfig->hcsdata.uarts[1].type == AEX_UART_RS232){
	// 	//设置 RS232 串口
	// 	aexgpio_onboard_set(SGWEC_CH2_SEL,1);
	// }else{
	// 	//设置 RS485 串口
	// 	aexgpio_onboard_set(SGWEC_CH2_SEL,0);
	// }
	struct uloop_fd fd_uart_uart5 = {
		.cb = read_uart5_callback,
		.fd = fd_uart5,
		.flags = ULOOP_READ,
	};
	fd_uart5 = com_open(pconfig->uart5_port,O_RDWR | O_NOCTTY | O_NONBLOCK);
	if(fd_uart5 > 0)
	{
		fd_uart_uart5.fd = fd_uart5;
		pconfig->hcsdata.uarts[1].uart_fd = fd_uart5;
		uloop_fd_add(&fd_uart_uart5, ULOOP_READ);
	}else{
		hcs_error("open uart5 error:%s", strerror(errno));
	}
	uloop_fd_add(&fd_net_tcp, ULOOP_READ);
	uloop_fd_add(&fd_net_udp, ULOOP_READ);

    ubus_init();

	uloop_run();

    uloop_timeout_cancel(&fd_timeout);
    uloop_done();
    CloseUart(pconfig);
    if(pconfig->hcsdata.uarts[0].type == 0 && pconfig->hcsdata.uarts[0].uart_fd != INVAILDSOCKET){
        close(pconfig->hcsdata.uarts[0].uart_fd);
    }
    if(pconfig->hcsdata.uarts[1].type == 0 && pconfig->hcsdata.uarts[1].uart_fd != INVAILDSOCKET){
        close(pconfig->hcsdata.uarts[1].uart_fd);
    }
#endif //USE_OPENWRT_UBUS
}

void rpc_smartgw_destory()
{
#ifdef USE_OPENWRT_UBUS
    hcs_hint("%s\r\n",__func__);
    ubus_destory();
#endif //USE_OPENWRT_UBUS
}
